﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Xml;
using System.ComponentModel;
using System.Reflection;

//using http://james.newtonking.com/json to read through json
namespace MediaRecommender.Model
{
    //When weve done formatting of the returned data we can get alot more information automagically
    class MediaData
    {
        //Api keys required to access data from media sites, goodreads.com and themoviedb.org
        private string TMDb_apiKey = "ffb2c95850522361084f2387925098f8";
        private string ISBNDb_apiKey = "252FGT1E";

        private string movieBaseURL = "http://api.themoviedb.org/3/";
        private string baseCoverUrl = "http://d3gtl9l2a4fn1j.cloudfront.net/t/p/original";
        private string gameBaseURL = "http://thegamesdb.net/api/";
        private string bookBaseURL = "http://isbndb.com/api/v2/xml/";

        //temp!
        public int TotalMovieCounter { get; private set; }
        private int TotalExpectet = 0;

        
        public MediaData()
        {
            bookBaseURL += ISBNDb_apiKey + "/";
        }

        
        private string getData(string URL)
        {
            //Heavily based on code snippet from; https://www.themoviedb.org/talk/52640d8f19c2951a08042ce6
            var request = (HttpWebRequest)WebRequest.Create(URL);
            request.Method = "GET";
            //request.Accept = "application/json";
            //request.Headers.Add("Accept-Charset", "UTF-8");
            request.ContentLength = 0;
            string responseContent = "";
            using (var response = request.GetResponse() as HttpWebResponse)
            {
                if (null != response && HttpStatusCode.OK == response.StatusCode)
                {
                    using (var reader = new StreamReader(response.GetResponseStream()))
                    {
                        responseContent = reader.ReadToEnd();
                    }
            }
                else return null; //Might need to implement a status code appropriate for error
            }

            return responseContent;
        }

        private List<Book> getXmlData(string xmlFormattedString)
        {
            List<Book> toReturn = new List<Book>();
            string title = "";
            List<Author> authors = new List<Author>();
            List<Genre> genres = new List<Genre>();

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(xmlFormattedString);

            XmlNodeList nodes = xmlDoc.DocumentElement.SelectNodes("//data");

            foreach (XmlNode node in nodes)
            {
                genres.Clear();
                authors.Clear();
                if (node.SelectSingleNode("author_data") != null)
                {
                    string author = node.SelectSingleNode("author_data").SelectSingleNode("name").InnerText;
                    authors.Add(new Author(author));
                }
                title = node.SelectSingleNode("title").InnerText;
                Genre currGenre;
                XmlNodeList genreNodes = node.SelectNodes("subject_ids");
                foreach (XmlNode genreNode in genreNodes)
                {
                    if (Enum.TryParse<Genre>(genreNode.InnerText.Replace("_", ""), out currGenre))
                        genres.Add(currGenre);
                }
                toReturn.Add(new Book(authors, title, genres));
            }
            return toReturn;
        }

        public Media getJsonData(string jsonFormattedString)
        {
            List<Genre> genres = new List<Genre>();
            string title = "";
            string director = "";
            List<Actor> actors = new List<Actor>();
            string coverImage = "";

            dynamic parsedObject = JsonConvert.DeserializeObject(jsonFormattedString);
            foreach (dynamic entry in parsedObject)
            {

                string name = entry.Name;
                dynamic value = entry.Value; 
                if (name.Equals("genres"))
                {
                    foreach (dynamic d in value)
                    {
                        string currValue = Convert.ToString(d);
                        string[] strings = currValue.Split('\"');
                        string prev = "";
                        for (int i = 0; i < strings.Length; i++)
                        {
                            if (prev.Equals("name"))
                            {
                                string temp = strings[i + 1].Replace(" ", "").Replace("-", "").ToLower();
                                genres.Add((Genre)Enum.Parse(typeof(Genre), temp));
                            }
                            prev = strings[i];
                        }
                    }
                }
                else if (name.Equals("original_title")) title = Convert.ToString(value);
                else if (name.Equals("poster_path")) coverImage = baseCoverUrl + Convert.ToString(value);
                else if (name.Equals("credits"))
                {
                    string valueString = Convert.ToString(value);
                    String[] sections = valueString.Split('[', ']');
                    //actors
                    string[] cast = sections[1].Split('{', '}');
                    foreach (string s in cast)
                        if (s.Contains("name"))
                        {
                            string[] actorInfo = s.Split(',');
                            string[] actorNameLine = actorInfo[1].Split(':');
                            actors.Add(new Actor(actorNameLine[1].Replace("\"", "").Trim()));
                            //Check if actor already exisists in our database
                        }

                    //director

                    string[] crew = sections[3].Split('{', '}');
                    if (crew.Length > 2)
                    {
                        string[] directorInfo = crew[1].Split(',');
                        string[] directorNameInfo = directorInfo[1].Split(':');
                        director = directorNameInfo[1].Replace("\"", "").Trim();
                    }
                }
            }

            //constructs movie object
            Director newDirector = new Director(director);
            Movie toReturn = new Movie(newDirector, actors, title, genres);
            foreach (Actor a in actors) a.AssociatedMedia.Add(toReturn);
            newDirector.AssociatedMedia.Add(toReturn);

            return toReturn;
        }

        #region movies
        //public string getActorsByMovie

        public Movie getMovieById(int id)
        {
            TotalExpectet++;
            string URL = string.Format("{2}movie/{0}?api_key={1}&append_to_response=credits", Convert.ToString(id), TMDb_apiKey, movieBaseURL);

            return (Movie)getJsonData(getData(URL));

            }

        public List<Movie> getMovieListByGenreId(int genreId, bool adultFilms, bool includeAll)
        {
            int pageNm = 1;
            List<Movie> movieList = new List<Movie>();
 
            string URLToFindPage = string.Format("{2}genre/{0}/movies?api_key={1}", //at the moment this list does not include; adult films or movies that have been rated ten or less times
                Convert.ToString(genreId),
                TMDb_apiKey,
                movieBaseURL,
                pageNm
                );
            dynamic objectToFindPages = JsonConvert.DeserializeObject(getData(URLToFindPage));
            foreach (dynamic entry in objectToFindPages)
            {
                string name = entry.Name;
                dynamic value = entry.Value;
                if (name.Equals("total_pages"))
                    pageNm = Convert.ToInt32(value);
                else if (name.Equals("total_results")) TotalMovieCounter += Convert.ToInt32(value);
        }

            for (int i = 1; i <= pageNm; i++)
        {
                string URL = string.Format("{2}genre/{0}/movies?api_key={1}&page={3}", //at the moment this list does not include; adult films or movies that have been rated ten or less times
                Convert.ToString(genreId), 
                TMDb_apiKey, 
                    movieBaseURL,
                    i
                );
                Console.WriteLine("Page number: " + i);
                dynamic parsedObject = JsonConvert.DeserializeObject(getData(URL));
                foreach (dynamic entry in parsedObject)
                {
                    string name = entry.Name;
                    dynamic value = entry.Value;
                    if (name.Equals("results"))
                    {
                        string valueString = Convert.ToString(value);
                        string[] sections = valueString.Split('{');
                        foreach (string s in sections)
                        {
                            string[] lines = s.Split(',');
                            if (lines.Length > 2)
                            {
                                string[] id = lines[2].Split(':');
                                movieList.Add(getMovieById(Convert.ToInt32(id[1].Trim())));
                                if (movieList.Count > 50) break;
                            }
                        }
                    }
                    if (movieList.Count > 50) break;
                }
                if (movieList.Count > 50) break;
            }
            Console.WriteLine("Total: " + TotalMovieCounter + "/ " + TotalExpectet);
            return movieList;
        }

        public List<Movie> getGenreIdList()
        {
            string URL = string.Format("{1}genre/list?api_key={0}", TMDb_apiKey, movieBaseURL);
            List<Movie> movieList = new List<Movie>();

            dynamic parsedObject = JsonConvert.DeserializeObject(getData(URL));
            foreach (dynamic entry in parsedObject)
            {

                string name = entry.Name;
                dynamic value = entry.Value;
                string valueString = Convert.ToString(value);
                string[] sections = valueString.Split('{');
                foreach (string s in sections)
                {
                    string[] infoId = s.Split(':');
                    if (infoId.Length > 2)
                    {
                        string[] id = infoId[1].Split(',');
                        movieList.AddRange(getMovieListByGenreId(Convert.ToInt32(id[0].Trim()), false, false));
                    }
                }
            }

            return movieList;
        }
        #endregion

        #region games
        public string getGameByName(string name)
        {
            //Example from here: http://forums.asp.net/t/1881390.aspx?Convert+XML+data+to+string+c+

            string xmlString = getData(gameBaseURL + "GetGame.php?name=" + name);

            //Create an XML Document and load an XML
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xmlString);

            //Get nodes
            XmlNodeList nodes = doc.DocumentElement.SelectNodes("//Game");
            //XmlNode nodes = doc.DocumentElement.SelectNodes("//Game");

            //This will build a string
            StringBuilder gameString = new StringBuilder();
            //gameString.Append("The Products available are : ");

            /*
            string title;
            List<Platform> platforms = new List<Platform>();
            GameStudio gamestudio;
            Publisher publisher;
            List<Genre> genre = new List<Genre>();

            foreach (XmlNode node in nodes)
            {
                if (node.SelectSingleNode("GameTitle").InnerText == name)
                {
                    title = node["GameTitle"].InnerText;
                    platforms.Add((Platform)Enum.Parse(typeof(Platform), node["Platform"].InnerText));
                    gamestudio = new GameStudio(node["Developer"].InnerText);
                    publisher = new Publisher(node["Publisher"].InnerText);
                if (node.SelectSingleNode("Genres") != null)
                {
                    XmlNode genreNode = node["Genres"];
                    foreach (XmlNode genre in genreNode.SelectNodes("genre"))
                        genre.Add((Genre)Enum.Parse(typeof(Genre), genre.InnerText);
                }
                }
            }

            //Creating a new VideoGame
            VideoGame vg = new VideoGame(gamestudio, publisher, platforms, title, genre);
            */


            //int count = 0;
            //Iterates through your String appending the available Names
            foreach (XmlNode node in nodes)
            {
                if (node.SelectSingleNode("GameTitle").InnerText == name)
                {
                gameString.Append("\n" + "Name: " + node["GameTitle"].InnerText);
                gameString.Append("\n" + "Platform: " + node["Platform"].InnerText);
                if (node.SelectSingleNode("Developer") != null) // if exists
                    gameString.Append("\n" + "Developer: " + node["Developer"].InnerText);
                if (node.SelectSingleNode("Publisher") != null) // if exists
                    gameString.Append("\n" + "Publisher: " + node["Publisher"].InnerText);
                    if (node.SelectSingleNode("Genres") != null)
                    {
                        XmlNode genreNode = node["Genres"];
                        foreach (XmlNode genre in genreNode.SelectNodes("genre"))
                            gameString.Append("\n" + genre.InnerText);
                    }
                gameString.Append("\n" + "-----------------------------------");
                }

            }
             

            //gameString.Append("\n" + "Result found: " + count);
            return gameString.ToString();
            //return vg;
        }


        public VideoGame getGameByName(string name, string platform)
        {
            //Example from here: http://forums.asp.net/t/1881390.aspx?Convert+XML+data+to+string+c+

            string xmlString = getData(gameBaseURL + "GetGame.php?name=" + name);

            //Create an XML Document and load an XML
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xmlString);

            //Get nodes
            XmlNodeList nodes = doc.DocumentElement.SelectNodes("//Game");
            //XmlNode nodes = doc.DocumentElement.SelectNodes("//Game");

            //This will build a string
            StringBuilder gameString = new StringBuilder();


            string title = null;
            List<Platform> platforms = new List<Platform>();
            string gamestudio = null;
            string publisher = null;
            List<Genre> genreList = new List<Genre>();

            foreach (XmlNode node in nodes)
            {
                if (node.SelectSingleNode("GameTitle").InnerText == name && node.SelectSingleNode("Platform").InnerText == platform)
                {
                    
                    title = node["GameTitle"].InnerText;
                    platforms.Add((Platform)Enum.Parse(typeof(Platform), node["Platform"].InnerText.Replace(" ", "")));
                    if (node.SelectSingleNode("Developer") != null)
                        gamestudio = node["Developer"].InnerText;
                    if (node.SelectSingleNode("Publisher") != null)
                        publisher = node["Publisher"].InnerText;
                    if (node.SelectSingleNode("Genres") != null)
                    {
                        XmlNode genreNode = node["Genres"];
                        foreach (XmlNode genre in genreNode.SelectNodes("genre"))
                            genreList.Add((Genre)Enum.Parse(typeof(Genre), genre.InnerText.Replace("-", "").Replace(" ", "")));
                    }
                    break;
            }

            }

            //constructs
            GameStudio newGameStudio = new GameStudio(gamestudio);
            Publisher newPublisher = new Publisher(publisher);
            VideoGame toReturn = new VideoGame(newGameStudio, newPublisher, platforms, title, genreList);
            return toReturn;
        }

        public string getGameById(int id)
        {
            return getData(gameBaseURL + "GetGame.php?name=" + Convert.ToString(id));
        }

        public List<VideoGame> getPlatformList(string platform)
        {
            int id = 0;

            string xmlString = getData(gameBaseURL + "GetPlatformsList.php");
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xmlString);

            XmlNodeList nodes = doc.DocumentElement.SelectNodes("//Platform");

            foreach (XmlNode node in nodes)
            {
                if (node.SelectSingleNode("name").InnerText == platform)
        {
                    id = Convert.ToInt32(node.SelectSingleNode("id").InnerText);
                }
            }

            return getGamesByPlatformId(id, platform);
        }

        public List<VideoGame> getGamesByPlatformId(int id, string platform)
        {
            List<VideoGame> toReturn = new List<VideoGame>();   
            string xmlString = getData(gameBaseURL + "GetPlatformGames.php?platform=" + Convert.ToString(id));
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xmlString);

            //Get nodes
            XmlNodeList nodes = doc.DocumentElement.SelectNodes("//Game");

            //This will build a string
            StringBuilder gameString = new StringBuilder();
            gameString.Append("The Products available are : ");

            string gameTitle = null;
            int count = 0;

            foreach (XmlNode node in nodes)
        {
                gameTitle = node.SelectSingleNode("GameTitle").InnerText;
                toReturn.Add(getGameByName(gameTitle, platform));
                count++;
                Console.Write("\r{0} Games Added to The LIST!!!!", count);
             }
            gameString.Append(count + " Games");
            Console.WriteLine(count);
            //return gameString.ToString();
            return toReturn;
        }
             
        #endregion

        #region books
        public Book GetBookByTitle(string title)
        {
            title = title.Replace(" ", "_");
            string URL = string.Format("{0}book/{1}",
                bookBaseURL,
                title
                );

            return getXmlData(getData(URL))[0];
        }

        public List<Media> GetBooksByGenre(string genre)
        {
            int totalPages = 0;

            string baseURL = string.Format("{0}books?q={1}",
                bookBaseURL,
                genre
                );
            string xmlData = getData(baseURL);
            List<Media> toReturn = new List<Media>();

            XmlDocument xmlDoc = new XmlDocument();

            xmlDoc.LoadXml(xmlData);

            XmlNode node = xmlDoc.SelectSingleNode("//page_count");
            totalPages = Convert.ToInt32(node.InnerText);
            Console.WriteLine(totalPages);
            toReturn.AddRange(getXmlData(xmlData));
            Console.Write("Got page 1, yayayya!");

            for (int i = 2; i <= 3; i++)
            {
                string pageNumber = "&page=" + i;
                string URL = baseURL + pageNumber;

                try
                {
                    toReturn.AddRange(getXmlData(getData(URL)));
                    Console.Write("\rGot page " + i + ", yayayya!");
                }
                catch (WebException e)
                {
                    Console.WriteLine("\nFAIIIIL!: " + e.Message);
                    Console.WriteLine();
                }

                
        }
            Console.WriteLine(baseURL);

            return toReturn;
        }

        public string GetAutherByName(string name)
        {
            string URI = "";

            return getData(URI);
        }
        #endregion
    }    
}
